﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using Emgu.CV;
using Emgu.CV.UI;
using Emgu.Util;
using Emgu.CV.Structure;

namespace SignDetection
{
    //双边缘结构体
    public struct LetterLine
    {
       public int x;
       public int y;
       public int begin;
       public int end;
       public int size;
    };

    class SignDetector2 : SignDetector
    {
        public override List<Bitmap> DetectSign(Bitmap src)
        {
            PlatePositionResult plate = PlatePositioning(src);
            return SegmentSigns(plate.Output);
        }
        public override PlatePositionResult PlatePositioning(Bitmap src)
        {
            Bitmap bmp_binary = this.binary(src);
            return this.GetLV(bmp_binary,src);
        }

        //旋转倾斜校正
        //hough变换
        int HoughTran(Bitmap binary)
        {
            const int MAXTHETA = 180;
            Image<Gray, Byte> Img = new Image<Gray, byte>(binary);

            int R = (int)(Math.Sqrt(binary.Height * binary.Height + binary.Width * binary.Width));
            int[] p = new int[R * MAXTHETA];
            for (int i = 0; i < binary.Width; i++)
                for (int j = binary.Height - 1; j >= 0; j--)
                {
                    int x = i;
                    int y = binary.Height - j - 1;
                    for (int theta = 0; theta < MAXTHETA; theta += 2)
                    {
                        //p=x*cos(theta)+y*sin(theta)
                        if (binary.GetPixel(i, j) == Color.FromArgb(0, 0, 0))
                        {
                            int p_index = (int)(x * Math.Cos(theta * Math.PI / 180) + y * Math.Sin(theta * Math.PI / 180));
                            if (p_index >= 0 && p_index < R)
                                p[p_index * MAXTHETA + theta]++;
                        }
                    }
                }
            int angle = -30;
            int count = p[0];
            for (int i = 1; i < R * MAXTHETA; i++)
            {
                if (count < p[i])
                {
                    count = p[i];
                    angle = i;
                }

            }
            angle = angle % MAXTHETA - 90;
            return angle;


        }
        //倾斜车牌校正
        public Bitmap HCorrect(int angle, Bitmap binary)
        {
            Bitmap bitmap = new Bitmap(binary.Width, binary.Height);
            for (int i = 0; i < binary.Width; i++)
                for (int j = 0; j < binary.Height; j++)
                {
                    //X'=x*cos(n)+y*sin(n)
                    //Y'=-x*sin(n)+y*cos(n)
                    int x = (int)(i * Math.Cos(angle * Math.PI / 180) + j * Math.Sin(angle * Math.PI / 180));
                    int y = (int)(i * Math.Sin(angle * Math.PI / 180) + j * Math.Cos(angle * Math.PI / 180));
                    // int x = i;
                    //int y = j;
                    if (x >= 0 && x < binary.Width && y >= 0 && y < binary.Height)
                        bitmap.SetPixel(x, y, binary.GetPixel(i, j));
                }
            return bitmap;
        }
        //字符分割
        public override List<Bitmap> SegmentSigns(Bitmap plate)
        {
            List<Bitmap> signs = new List<Bitmap>();
            //foreach signs.Add(signs)
            signs = Recognize(plate);
            return signs;
        }
        //图片二值化
        private Bitmap binary(Bitmap ori_Bitmap)
        {
            Bitmap bitmap = new Bitmap(ori_Bitmap.Width, ori_Bitmap.Height);
            Image<Bgr, byte> RGB_Img = new Image<Bgr, byte>(ori_Bitmap);
            Image<Bgr, byte> YUV_Img = new Image<Bgr, byte>(ori_Bitmap);
            //将图像从RGB空间转换到YUV空间
            CvInvoke.cvCvtColor(RGB_Img, YUV_Img, Emgu.CV.CvEnum.COLOR_CONVERSION.CV_BGR2YCrCb);
            for (int i = 0; i < YUV_Img.Width; i++)
                for (int j = 0; j < YUV_Img.Height; j++)
                {
                    Bgr c = YUV_Img[j, i];
                    //根据Cr色调值对原彩色图像进行二值化
                    if (c.Red > 150)
                        bitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255));
                    else
                        bitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0));
                }

            return bitmap;

        }
        //定位及分割车牌
        private PlatePositionResult GetLV(Bitmap binary_Img, Bitmap ori_bitmap)
        {
            Bitmap LV = new Bitmap(binary_Img.Width, binary_Img.Height);
            Image<Gray, byte> src = new Image<Gray, byte>(binary_Img);
            //对二值化后的图像进行轮廓查找 
            Contour<Point> contours = src.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL);
            PlatePositionResult PPR=new PlatePositionResult();
            Rectangle rect=new Rectangle();
            bool mark = false;
            for (; contours != null;
                contours = contours.HNext)
            {
               rect = contours.BoundingRectangle;
              
               
                //根据车牌的形状特征对找到的轮廓进行形状判断
                if (rect.Width > rect.Height && rect.Width * rect.Height < 10000 && rect.Width * rect.Height > 1200)
                {
                    double range = rect.Width / (double)rect.Height;
                    double std_range = 440 / (double)140;
                    if (range >= std_range * 0.8 && range <= std_range * 1.2)
                    {
                        LV = ori_bitmap.Clone(rect, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                        mark = true;
                    }
                    else
                        mark = false;
                   
                    
                }
            }
            //返回原彩色图片中找到的车牌部分图像
            PPR.Output = LV;
            PPR.Correct = mark;
            PPR.Position = rect;
            return PPR;

        }
        //分割字符
        private List<Bitmap> Recognize(Bitmap platebitmap)
        {
            List<Bitmap> char_Bit = new List<Bitmap>();
            List<Rectangle> rect = new List<Rectangle>();
            
            Image<Bgr, byte> show = new Image<Bgr, byte>(platebitmap);
            Image<Gray, byte> gray = new Image<Gray, byte>(platebitmap);
            Bitmap grayBitmap = gray.ToBitmap();
            //Image<Gray, byte> gray = new Image<Gray, byte>(cl);
            //采用ostu自动阈值分割算法对定位的车牌图像进行二值化
            Bitmap cl = otsuThreshold(gray);
            //采用Kmeans算法对定位的车牌图像进行二值化
            //Bitmap cl = cluster(platebitmap);
            gray = new Image<Gray, byte>(cl);
            Contour<Point> contour = gray.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL);
            for (; contour != null; contour = contour.HNext)
            {
                Rectangle r = contour.BoundingRectangle;

                if (r.Height > r.Width && r.Width * r.Height > 64)
                {
                    rect.Add(r);
                }
            }
            //FindContours轮廓函数七个字符都找到
            //对找到的七个字符从左到右排序
            List<Rectangle> char_rect = new List<Rectangle>();
            if (rect.Count == 7)
            {
                int[] index = new int[rect.Count];
                for (int i = 0; i < rect.Count; i++)
                    index[i] = rect[i].X;
                for (int i = 0; i < rect.Count; i++)
                    for (int j = 0; j < rect.Count - i - 1; j++)
                    {
                        if (index[j] > index[j + 1])
                        {
                            int temp = index[j];
                            index[j] = index[j + 1];
                            index[j + 1] = temp;
                            System.Console.WriteLine(index[j + 1]);
                        }
                    }
                for (int i = 0; i < rect.Count; i++)
                {
                    for (int j = 0; j < rect.Count; j++)
                    {
                        if (index[i] == rect[j].X)
                            char_rect.Add(rect[j]);
                    }
                }
            }

            //FindContours轮廓函数一个字符都没有找到
            //根据车牌的几何特征进行分割
            if (rect.Count != 7)
            {
                char_rect.Clear();
                int x = 0;
                int y = 0;
                int width = (int)(platebitmap.Width * 45 / 440);
                int height = platebitmap.Height;
                x = (int)(platebitmap.Width * 15 / 440);
                char_rect.Add(new Rectangle(x, y, width, height));
                x = (int)(platebitmap.Width * 72 / 440);
                char_rect.Add(new Rectangle(x, y, width, height));
                x = (int)(platebitmap.Width * 146 / 440);
                char_rect.Add(new Rectangle(x, y, width, height));
                x = (int)(platebitmap.Width * 203 / 440);
                char_rect.Add(new Rectangle(x, y, width, height));
                x = (int)(platebitmap.Width * 260 / 440);
                char_rect.Add(new Rectangle(x, y, width, height));
                x = (int)(platebitmap.Width * 313 / 440);
                char_rect.Add(new Rectangle(x, y, width, height));
                x = (int)(platebitmap.Width * 370 / 440);
                char_rect.Add(new Rectangle(x, y, width, height));

            }

            ////FindContours轮廓函数找到一个及多个字符
            //if (rect.Count != 0&&rect.Count!=7)
            //{
            //    int x = rect[0].X;
            //    int y = rect[0].Y;
            //    int width = (int)(rect[0].Height/2);
            //    int height = rect[0].Height;
            //    rect.Clear();
            //}
            //根据分割得到的矩形提取车牌字符
            for (int i = 0; i < char_rect.Count; i++)
            {
                Bitmap charbit = grayBitmap.Clone(char_rect[i], System.Drawing.Imaging.PixelFormat.DontCare);
                Bitmap std_charbit = (Bitmap)charbit.GetThumbnailImage(16, 32, null, IntPtr.Zero);
                Bitmap std_gray=normalize(std_charbit);
                char_Bit.Add(std_gray);
                CvInvoke.cvRectangle(show, new Point(char_rect[i].X, char_rect[i].Y),
                new Point(char_rect[i].X + char_rect[i].Width, char_rect[i].Y + char_rect[i].Height), new MCvScalar(0, 255, 0), 1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, 0);
            }
            

            if (base.Monitor != null)
                base.Monitor.PictureBoxB.Image = show.ToBitmap();
            if (base.Monitor != null)
                base.Monitor.PictureBoxA.Image = gray.ToBitmap() ;


            //Bitmap vert_Img = Vert_DoubEdge(cl, 5, 25);//垂直双边缘检测
            //Bitmap hori_Img = Hori_DoubEdge(cl, 5, 25);//水平双边缘检测
            //if (base.Monitor != null)
            //    base.Monitor.PictureBox_Hori.Image = hori_Img;
            //if (base.Monitor != null)
            //    base.Monitor.PictureBox_Vert.Image = vert_Img;

            //返回分割得到的车牌字符链表
            return char_Bit;

        }
        //用kmeans方法二值化图像
        private Bitmap cluster(Bitmap LV)
        {
            int SampleCount = LV.Width * LV.Height;

            IntPtr points = CvInvoke.cvCreateMat(SampleCount, 3, Emgu.CV.CvEnum.MAT_DEPTH.CV_32F);
            IntPtr label = CvInvoke.cvCreateMat(SampleCount, 1, Emgu.CV.CvEnum.MAT_DEPTH.CV_32S);
            IntPtr center = CvInvoke.cvCreateMat(2, 3, Emgu.CV.CvEnum.MAT_DEPTH.CV_32F);
            for (int i = 0; i < LV.Width; i++)
                for (int j = 0; j < LV.Height; j++)
                {
                    int k = j * LV.Width + i;
                    MCvScalar element = new MCvScalar(LV.GetPixel(i, j).R);
                    CvInvoke.cvSet2D(points, k, 0, element);
                    element.v0 = LV.GetPixel(i, j).G;
                    CvInvoke.cvSet2D(points, k, 1, element);
                    element.v0 = LV.GetPixel(i, j).B;
                    CvInvoke.cvSet2D(points, k, 2, element);
                }

            CvInvoke.cvKMeans2(points, 2, label, new MCvTermCriteria(10, 1), 5, IntPtr.Zero, 0, center, IntPtr.Zero);
            
            Bitmap bitmap = new Bitmap(LV.Width, LV.Height);
            double r1 = CvInvoke.cvGet2D(center, 0, 0).v0;
            double g1 = CvInvoke.cvGet2D(center, 0, 1).v0;
            double b1 = CvInvoke.cvGet2D(center, 0, 2).v0;
            double r2 = CvInvoke.cvGet2D(center, 1, 0).v0;
            double g2 = CvInvoke.cvGet2D(center, 1, 1).v0;
            double b2 = CvInvoke.cvGet2D(center, 1, 2).v0;
            if (b1 > b2)
            {
                for (int i = 0; i < LV.Width; i++)
                    for (int j = 0; j < LV.Height; j++)
                    {
                        int k = j * LV.Width + i;
                        if (CvInvoke.cvGet2D(label, k, 0).v0 == 1)
                            bitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0));
                        else
                            bitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255));
                    }
            }
            else
            {
                for (int i = 0; i < LV.Width; i++)
                    for (int j = 0; j < LV.Height; j++)
                    {
                        int k = j * LV.Width + i;
                        if (CvInvoke.cvGet2D(label, k, 0).v0 == 0)
                            bitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0));
                        else
                            bitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255));
                    }
            }
            return bitmap;
        }

        //大津法ostu自动分割车牌图片
        private Bitmap otsuThreshold(Image<Gray, byte> gray)
        {
            int width = gray.Width;

            int height = gray.Height;
            const int GrayScale = 256;
            int[] pixelCount = new int[GrayScale];
            float[] pixelPro = new float[GrayScale];
            int i, j, pixelSum = width * height, threshold = 0;
            for (i = 0; i < GrayScale; i++)
            {
                pixelCount[i] = 0;
                pixelPro[i] = 0;
            }

            //统计灰度级中每个像素在整幅图像中的个数  
            for (i = 0; i < height; i++)
            {
                for (j = 0; j < width; j++)
                {
                    pixelCount[(int)gray[i, j].MCvScalar.v0]++;
                }
            }
            //计算每个像素在整幅图像中的比例  
            for (i = 0; i < GrayScale; i++)
            {
                pixelPro[i] = (float)pixelCount[i] / pixelSum;
            }

            //遍历灰度级[0,255]  
            float w0, w1, u0tmp, u1tmp, u0, u1, u,
                  deltaTmp, deltaMax = 0;
            for (i = 0; i < GrayScale; i++)
            {
                w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;
                for (j = 0; j < GrayScale; j++)
                {
                    if (j <= i)   //背景部分  
                    {
                        w0 += pixelPro[j];
                        u0tmp += j * pixelPro[j];
                    }
                    else   //前景部分  
                    {
                        w1 += pixelPro[j];
                        u1tmp += j * pixelPro[j];
                    }
                }
                u0 = u0tmp / w0;
                u1 = u1tmp / w1;
                u = u0tmp + u1tmp;
                deltaTmp = (float)(w0 * Math.Pow((u0 - u), 2) + w1 * Math.Pow((u1 - u), 2));
                if (deltaTmp > deltaMax)
                {
                    deltaMax = deltaTmp;
                    threshold = i;
                }
            }
            Bitmap bitmap = new Bitmap(gray.Width, gray.Height);
            for (i = 0; i < height; i++)
            {

                for (j = 0; j < width; j++)
                {
                    if ((int)gray[i, j].MCvScalar.v0 >= threshold)
                        bitmap.SetPixel(j, i, Color.FromArgb(255, 255, 255));
                    else
                        bitmap.SetPixel(j, i, Color.FromArgb(0, 0, 0));
                }

            }
            return bitmap;
        }
        
        
        //垂直双边缘检测
        public Bitmap Vert_DoubEdge(Bitmap binary_LV, int MinSize, int MaxSize)  
        {
            List<LetterLine> LetterLines=new List<LetterLine>();
            Bitmap Vertical_Img = new Bitmap(binary_LV.Width,binary_LV.Height);
            
		        int ysize = 0;
                int end = 0;
                int begin = 0;
                int x = 0, y = 0;
		        for (x = 0; x < binary_LV.Width; x++)
                {
                    
                    y = 0;
			        Color pixel =binary_LV.GetPixel(x,y);
                    begin = -1;
			        while (y <binary_LV.Height - 1)
                    {
                        Vertical_Img.SetPixel(x,y,Color.FromArgb(0,0,0));
                        Color next =binary_LV.GetPixel(x,y+1);
                        if (begin == -1)
                        {
                            if (pixel != next)
                                begin = y;
                        }
                        else
                        {
                            if (y - begin > MaxSize)
                                begin = -1;
                            else if (pixel != next)
                            {
                                end = y;
                                ysize = end - begin;
                                if (MinSize < ysize)
                                {
                                    LetterLine letterline;
                                    letterline.x = x;
                                    letterline.y = y;
                                    letterline.begin = begin;
                                    letterline.end = end;
                                    letterline.size = end - begin;
							        LetterLines.Add(letterline);
                                }
                                begin = -1;
                            }
                        }
                        y++;
                    }//end while
                }//end for

                int[] His=new int[200];
		        for (int i = 0; i < LetterLines.Count; i++)//寻找宽度的直方图
                {
                    LetterLine Line = LetterLines[i];
                    His[Line.size]++;
                }
                //寻找宽度直方图的峰值
                int max_w = His[0];
                for (int i = 1; i < 200; i++)
                {
                    if (max_w < His[i])
                        max_w = His[i];
                }//搜索笔画宽度
                   
		        for (int i = 0; i < LetterLines.Count; i++)
                    {
                        LetterLine Line = LetterLines[i];
                        //if (Line.size >= max_w - 5 && Line.size <= max_w + 5)
                        
                            for (int m = Line.begin; m < Line.end; m++)
                                Vertical_Img.SetPixel(Line.x, m, Color.FromArgb(255, 255, 255));

        					
                    }
                return Vertical_Img;
        }
        //水平双边缘检测
        public Bitmap Hori_DoubEdge(Bitmap binary_LV, int MinSize, int MaxSize)
        {
    		List<LetterLine> LetterLines=new List<LetterLine>();
            Bitmap Hori_Img =new Bitmap(binary_LV.Width,binary_LV.Height);
    			int xsize = 0;
                int end = 0;
                int begin = 0;
                int x = 0, y = 0;
    			for (y = 0; y < binary_LV.Height; y++)
                {
                    x = 0;
    				Color pixel =binary_LV.GetPixel(x,y);
                    begin = -1;
    				while (x < binary_LV.Width - 1)
                    {
                        Hori_Img.SetPixel(x, y, Color.FromArgb(0, 0, 0));
                        Color next = binary_LV.GetPixel(x+1,y);
                        if (begin == -1)
                        {
                            if (pixel != next)
                                begin = x;
                        }
                        else
                        {
                            if (x - begin > MaxSize)
                                begin = -1;
                            else if (pixel != next)
                            {
                                end = x;
                                xsize = end - begin;
                                if (MinSize < xsize)
                                {
                                    LetterLine letterline;
                                    letterline.x = x;
                                    letterline.y = y;
                                    letterline.begin = begin;
                                    letterline.end = end;
                                    letterline.size = end - begin;
                                    LetterLines.Add(letterline);
                                }
                                begin = -1;
                            }
                        }
                        x++;
                    }//end while
                }//end for
    
                int[] His=new int[200];
    			for (int i = 0; i < LetterLines.Count; i++)//寻找宽度的直方图
                {
                    LetterLine Line = LetterLines[i];
                    His[Line.size]++;
                }
                //寻找宽度直方图的峰值
                int max_w = His[0];
                for (int i = 1; i < 200; i++)
                {
                    if (max_w < His[i])
                        max_w = His[i];
                }//搜索笔画宽度

                for (int i = 0; i < LetterLines.Count; i++)
                    {
                        LetterLine Line = LetterLines[i];
                        //if (Line.size >= max_w - 5 && Line.size <= max_w + 5)//根据出现频率最多字符宽度值检测双边缘

                        for (int m = Line.begin; m < Line.end; m++)
                            Hori_Img.SetPixel(m, Line.y, Color.FromArgb(255, 255, 255));
    						
                    }
                return Hori_Img;
        }
        private Bitmap normalize(Bitmap bitmap)
        {
            Bitmap dst = new Bitmap(bitmap.Width,bitmap.Height);
            Color pixel = bitmap.GetPixel(0,0);
            int minGray = pixel.R;
            int maxGray = pixel.R;
            for (int i = 0; i < bitmap.Width; i++)
                for (int j = 0; j < bitmap.Height; j++)
                {
                    if (minGray > bitmap.GetPixel(i,j).R)
                        minGray = bitmap.GetPixel(i,j).R;
                    if (maxGray < bitmap.GetPixel(i,j).R)
                        maxGray = bitmap.GetPixel(i, j).R;
                }
            for (int i = 0; i < bitmap.Width; i++)
                for (int j = 0; j < bitmap.Height; j++)
                {
                    int gray=(int)((bitmap.GetPixel(i,j).R - minGray) * 255 / (maxGray - minGray));
                    dst.SetPixel(i,j,Color.FromArgb(gray,gray,gray));
                }
            return dst;
        }
        }
}
